#include "sleefileprocessor.h"
#include "QFileInfo"
#include <QDir>
#include <QDateTime>
#include <QDebug>

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

bool SleeFileProcessor::get_fs_stat(const QString &qstr_fso, struct stat64 &stat_fso)
{
    QDir qdr(qstr_fso);
    while(!qdr.exists())
    {
        if(!qdr.cd("../"))
        {
            if(!qdr.exists()) return false;
        }
    }

    int res = stat64(qdr.absolutePath().toLocal8Bit().data(), &stat_fso);
    if(res) return false;
    return true;
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

SleeFileProcessor::SleeFileProcessor(QObject *parent) :
    QThread(parent),
    m_operations_cntr(0),
    m_bytes_total(0),
    m_bytes_done(0),
    m_do_pause(false),
    m_do_reset(false)
{
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

void SleeFileProcessor::enqueue_item(const QString &qstr_fso_src, const QString &qstr_fso_dest, int operation)
{
    QMutexLocker locker(&mqmx_queue_access);
    m_operations_cntr ++;
    mqh_sources[m_operations_cntr] = qstr_fso_src;
    mqh_destinations[m_operations_cntr] = qstr_fso_dest;
    QPair<int, int> new_op(m_operations_cntr, operation);

    mq_operations.enqueue(new_op);
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

void SleeFileProcessor::add_op_to_copy(const QString &qstr_fso_src, const QString &qstr_fso_dest, int operation)
{
    QFileInfo qfi_(qstr_fso_src);
    if(qfi_.isDir())
    {
        enqueue_item(qstr_fso_src, qstr_fso_dest, FSOMakeDir);
        QDir qdir(qstr_fso_src);
        QFileInfoList qfil_entries = qdir.entryInfoList(QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot|QDir::Hidden|QDir::System);
        foreach(QFileInfo qfi_entry, qfil_entries)
        {
            QString qstr_fso_new_src = qfi_entry.absoluteFilePath();
            QString qstr_fso_new_dest = qstr_fso_dest + "/" + qfi_entry.fileName();
            add_op_to_copy(qstr_fso_new_src, qstr_fso_new_dest, operation);
        }
    }
    else
    {
        enqueue_item(qstr_fso_src, qstr_fso_dest, operation);
        m_bytes_total += qfi_.size();
    }
    if(operation == FSOMove)
        enqueue_item(qstr_fso_src, qstr_fso_src, FSODelete);
}


//--------------------------------------------------------------------------------------------------------------------------------------------------------------

bool SleeFileProcessor::add_to_copy(const QString &qstr_fso_src, const QString &qstr_fso_dest)
{
    add_op_to_copy(qstr_fso_src, qstr_fso_dest, FSOCopy);
    return true;
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

bool SleeFileProcessor::add_to_move(const QString &qstr_fso_src, const QString &qstr_fso_dest)
{
    struct stat64 stat_dest;
    struct stat64 stat_src;
    if(!get_fs_stat(qstr_fso_dest, stat_dest))
    {
        qDebug()<<Q_FUNC_INFO<<" destination FS object is unaccessible:"<<qstr_fso_dest;
        //TO DO: SET POLICY
        return false;
    }
    if(!get_fs_stat(qstr_fso_src, stat_src))
    {
        qDebug()<<Q_FUNC_INFO<<" source FS object is unaccessible:"<<qstr_fso_src;
        return false;
    }

    if(stat_dest.st_dev == stat_src.st_dev)
    {
        if(!QFile::rename(qstr_fso_src, qstr_fso_dest))
        {
            qDebug()<<Q_FUNC_INFO<<" moving failed";
            //TO DO: SET POLICY
            return false;
        }
        emit queue_item_done(qstr_fso_src, qstr_fso_dest);
        return false;
    }
    add_op_to_copy(qstr_fso_src, qstr_fso_dest, FSOMove);
    return true;

}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

bool SleeFileProcessor::add_to_del(const QString &qstr_fso_src)
{
    QFileInfo qfi_(qstr_fso_src);
    if(qfi_.isDir())
    {
        QDir qdir(qstr_fso_src);
        QFileInfoList qfil_entries = qdir.entryInfoList(QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot|QDir::Hidden|QDir::System);
        foreach(QFileInfo qfi_entry, qfil_entries)
        {
            QString qstr_fso_new_src = qfi_entry.absoluteFilePath();
            add_to_del(qstr_fso_new_src);
        }
    }
    enqueue_item(qstr_fso_src, qstr_fso_src, FSODelete);
    m_bytes_total += qfi_.size();
    return true;
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

bool SleeFileProcessor::operation_makedir(const QString &qstr_fso_dest)
{
    if(!QDir().mkpath(qstr_fso_dest))
    {
        qDebug()<<Q_FUNC_INFO<<" unable to create: "<<qstr_fso_dest;
        return false;
    }

    return true;
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

bool SleeFileProcessor::operation_delete(const QString &qstr_fso)
{
    QFileInfo qfi(qstr_fso);
    if(qfi.isDir())
    {
        if(!QDir().rmdir(qstr_fso))
        {
            qDebug()<<Q_FUNC_INFO<<" unable to delete directory: "<<qstr_fso;
            return false;
        }
    }
    else
    {
        if(!QFile::remove(qstr_fso))
        {
            qDebug()<<Q_FUNC_INFO<<" unable to delete: "<<qstr_fso;
            return false;
        }
    }

    return true;
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------

bool SleeFileProcessor::operation_copy(const QString &qstr_fso_src, const QString &qstr_fso_dest)
{
    static char buffer[4096];

    QFile qf_src(qstr_fso_src);
    QFile qf_dest(qstr_fso_dest);
    if(qf_dest.exists())
    {
        qDebug()<<Q_FUNC_INFO<<"file: "<<qf_dest.fileName()<<" already exists!!!! ";
        //TO DO: SET POLICY
        return false;
    }


    if(!qf_src.open(QIODevice::ReadOnly))
    {
        qDebug()<<Q_FUNC_INFO<<" uable to open file:"<<qf_src.fileName()<<". reason:"<<qf_src.errorString();
        //TO DO: SET POLICY
        return false;
    }
    if(!qf_dest.open(QIODevice::WriteOnly))
    {
        qDebug()<<Q_FUNC_INFO<<" uable to open file:"<<qf_dest.fileName()<<". reason:"<<qf_dest.errorString();
        //TO DO: SET POLICY
        return false;
    }
    qint64 bytes_done = 0;
    qint64 src_size = QFileInfo(qstr_fso_src).size();
    quint64 time_tick = QDateTime::currentMSecsSinceEpoch();
    while(1)
    {
        qint64 bytes_read = qf_src.read(buffer, 4096);
        if(bytes_read < 0)
        {
            qDebug()<<Q_FUNC_INFO<<qf_src.fileName()<<qf_src.errorString();
            //TO DO: SET POLICY
            return false;
        }
        qint64 bytes_written = qf_dest.write(buffer, bytes_read);
        if(bytes_written != bytes_read)
        {
            qDebug()<<Q_FUNC_INFO<<qf_dest.fileName()<<qf_dest.errorString();
            //TO DO: SET POLICY
            return false;
        }
        m_bytes_done += bytes_read;
        bytes_done += bytes_read;
        if(bytes_read < 4096)
        {
            break;
        }
        do_pause();
        if(m_do_reset) break;

        quint64 now_tick = QDateTime::currentMSecsSinceEpoch();

        if((now_tick - time_tick) > 200)
        {
            time_tick = now_tick;
            emit queue_item_bytes_processed(bytes_done, src_size);
            emit queue_bytes_processed(m_bytes_done, m_bytes_total);
        }
    }
    qf_src.close();
    qf_dest.close();
    emit queue_item_bytes_processed(bytes_done, src_size);
    emit queue_bytes_processed(m_bytes_done, m_bytes_total);
    return true;
}


//--------------------------------------------------------------------------------------------------------------------------------------------------------------


void SleeFileProcessor::run()
{

    QString qstr_fso_src;
    QString qstr_fso_dest;
    FSOOperations operation;
    int item_cntr = 0;
    emit queue_started(mqh_sources.size(), m_bytes_total);

    while(1)
    {
        if(m_do_reset) break;
        do_pause();
        mqmx_queue_access.lock();
        if(mq_operations.isEmpty())
        {
            break;
        }
        QPair<int, int> curr_op;
        curr_op = mq_operations.dequeue();
        qstr_fso_src = mqh_sources.value(curr_op.first);
        qstr_fso_dest = mqh_destinations.value(curr_op.first);
        operation = (FSOOperations) curr_op.second;
        item_cntr ++;
        mqmx_queue_access.unlock();
        do_pause();
        if(m_do_reset) break;

        emit queue_item_started(qstr_fso_src, qstr_fso_dest, operation);

        if(operation == FSOMakeDir)
        {
            if(!operation_makedir(qstr_fso_dest)) break;
        }
        else if(operation == FSODelete)
        {
            if(!operation_delete(qstr_fso_src)) break;
        }
        else if(operation == FSOCopy)
        {
            if(!operation_copy(qstr_fso_src, qstr_fso_dest)) break;
        }

        emit queue_item_done(qstr_fso_src, qstr_fso_dest);
        emit queue_fso_processed(item_cntr, m_operations_cntr);
    }
    emit queue_done();
    mqh_sources.clear();
    mqh_destinations.clear();
    mq_operations.clear();
    m_operations_cntr = 0;
    m_bytes_total = 0;
    m_bytes_done = 0;
    mqmx_queue_access.unlock();
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
